﻿using BCGPSCommon;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace BCGPSMongoData
{
    public class MongoDBCommandMultCollect<T>
    {
        private IMongoClient client;
        private IMongoDatabase database;
        private Dictionary<string, IMongoCollection<T>> DictCollection;
        private static readonly IConfigurationRoot configuration;

        static MongoDBCommandMultCollect()
        {
            configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .Build();
            var a = Directory.GetCurrentDirectory();
        }
        public MongoDBCommandMultCollect(List<string> collectionName)
        { 
            this.client = new MongoClient(  configuration["MongoDB:ConnectionString"]);
            this.database = client.GetDatabase( configuration["MongoDB:Database"]);
            DictCollection = new Dictionary<string, IMongoCollection<T>>();
            foreach (var item in collectionName)
            {
                DictCollection.Add(item, database.GetCollection<T>(item));
            } 
        } 

        public Task InsertDocumentAsync(string collectionName, T document)
        {
            return DictCollection[collectionName].InsertOneAsync(document);
        }

        public Task InsertDocumentAsync(string collectionName, IEnumerable<T> documents)
        {
            return DictCollection[collectionName].InsertManyAsync(documents);
        }

        public Task<DeleteResult> DeleteDocumentAsync(string collectionName, Expression<Func<T, bool>> func)
        {
            return DictCollection[collectionName].DeleteOneAsync(func);
        }

        public Task<DeleteResult> DeleteDocumentAsync(string collectionName, FilterDefinition<T> filter)
        {
            return DictCollection[collectionName].DeleteOneAsync(filter);
        }
        public Task<DeleteResult> DeleteDocumentsAsync(string collectionName, Expression<Func<T, bool>> func)
        {
            return DictCollection[collectionName].DeleteManyAsync(func);
        }

        public Task<DeleteResult> DeleteDocumentsAsync(string collectionName, FilterDefinition<T> filter)
        {
            return DictCollection[collectionName].DeleteManyAsync(filter);
        }
        public Task<T> UpdateDocumentAsync(string collectionName, Expression<Func<T, bool>> func, UpdateDefinition<T> update)
        {
            FindOneAndUpdateOptions<T> options = new FindOneAndUpdateOptions<T>
            {
                IsUpsert = false,
                ReturnDocument = ReturnDocument.After
            };

            return DictCollection[collectionName].FindOneAndUpdateAsync(func, update, options);
        }

        public Task<UpdateResult> UpdateDocumentsAsync(string collectionName, Expression<Func<T, bool>> func, UpdateDefinition<T> update)
        {
            return DictCollection[collectionName].UpdateManyAsync(func, update);
        }

        public Task<T> ReplaceDocumentAsync(string collectionName, Expression<Func<T, bool>> func, T document)
        {
            return DictCollection[collectionName].FindOneAndReplaceAsync(func, document);
        }


        public T ReplaceDocument(string collectionName, Expression<Func<T, bool>> func, T document)
        {
            return DictCollection[collectionName].FindOneAndReplace(func, document);
        }


        public Task<T> SelectDocumentAsync(string collectionName, Expression<Func<T, bool>> func)
        {
            return DictCollection[collectionName].Find(func).FirstOrDefaultAsync();
        }
        public Task<T> SelectDocumentAsync(string collectionName, Expression<Func<T, bool>> func, Expression<Func<T, object>> sortField, bool descending)
        {
            if (descending)
            {
                return DictCollection[collectionName].Find(func).SortByDescending(sortField).FirstOrDefaultAsync();
            }
            else
            {
                return DictCollection[collectionName].Find(func).SortBy(sortField).FirstOrDefaultAsync();
            }

        }
         
        public Task<T> SelectDocumentAsync(string collectionName, FilterDefinition<T> filter)
        {
            return DictCollection[collectionName].Find(filter).FirstOrDefaultAsync();
        }

        public Task<List<T>> SelectDocumentsAsync(string collectionName, Expression<Func<T, bool>> func)
        {
            return DictCollection[collectionName].Find(func).ToListAsync();
        }
        public Task<List<T>> SelectDocumentsAsync(string collectionName, FilterDefinition<T> filter)
        {
            return DictCollection[collectionName].Find(filter).ToListAsync();
        }
        public Task<List<TResult>> SelectDocumentsAsync<TResult>(string collectionName, FilterDefinition<T> filter, ProjectionDefinition<T, TResult> fields)
        {
            return DictCollection[collectionName].Find(filter).Project(fields).ToListAsync();
        }
        public Task<List<TResult>> SelectDocumentsAsync<TResult>(string collectionName, Expression<Func<T, bool>> func, ProjectionDefinition<T, TResult> fields)
        {
            return DictCollection[collectionName].Find(func).Project(fields).ToListAsync();
        }
        public async Task<PageableList<T>> SelectDocumentsAsync(string collectionName, Expression<Func<T, bool>> func, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<T> pageableList = new PageableList<T>
            {
                Count = await DictCollection[collectionName].CountAsync(func)
            };

            if (descending)
            {
                pageableList.Items = await DictCollection[collectionName].Find(func).SortByDescending(sortField).Skip(skip).Limit(limit).ToListAsync();
            }
            else
            {
                pageableList.Items = await DictCollection[collectionName].Find(func).SortBy(sortField).Skip(skip).Limit(limit).ToListAsync();
            }

            return pageableList;
        }

        public async Task<PageableList<T>> SelectDocumentsAsync(string collectionName, FilterDefinition<T> filter, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<T> pageableList = new PageableList<T>
            {
                Count = await DictCollection[collectionName].CountAsync(filter)
            };

            if (descending)
            {
                pageableList.Items = await DictCollection[collectionName].Find(filter).SortByDescending(sortField).Skip(skip).Limit(limit).ToListAsync();
            }
            else
            {
                pageableList.Items = await DictCollection[collectionName].Find(filter).SortBy(sortField).Skip(skip).Limit(limit).ToListAsync();
            }

            return pageableList;
        }
        public async Task<PageableList<TResult>> SelectDocumentsAsync<TResult>(string collectionName, Expression<Func<T, bool>> func, ProjectionDefinition<T, TResult> fields, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<TResult> pageableList = new PageableList<TResult>
            {
                Count = await DictCollection[collectionName].CountAsync(func)
            };

            if (descending)
            {
                pageableList.Items = await DictCollection[collectionName].Find(func).SortByDescending(sortField).Skip(skip).Limit(limit).Project(fields).ToListAsync();
            }
            else
            {
                pageableList.Items = await DictCollection[collectionName].Find(func).SortBy(sortField).Skip(skip).Limit(limit).Project(fields).ToListAsync();
            }

            return pageableList;
        }
        public Task<T> RunCommandAsync(string collectionName, Command<T> command)
        {
            return database.RunCommandAsync(command);
        }
    }
}
